/* eslint-disable no-undef */
import { Message } from 'element-ui'
import { get } from 'lodash'
import { CONTENT_TYPE_MAP } from './constance'
import send from '../store/api/http'

export default class Upload {
  constructor (config) {
    config = {
      ...config,
      initMap: {
        ...(config.init || {})
      }
    }
    config.init = this.initInputChange
    Object.entries(config).forEach(([key, val]) => {
      this[key] = val
    })
    this.http_method = config.http_method || 'PUT'
    this.features = config.features || {}
  }

  // FAILED = 4

  // FILE_SIZE_ERROR = -600

  // FILE_EXTENSION_ERROR = -601

  // HTTP_ERROR = -200

  // SECURITY_ERROR = -400

  // GENERIC_ERROR = -100

  // IO_ERROR = -300

  // INIT_ERROR = -500

  getBrowseBtn () {
    return typeof this.browse_button === 'string' ? document.querySelector(`#${this.browse_button}`) : this.browse_button
  }

  disableInput () {
    const btnList = document.querySelectorAll('.upload_file_input')
    btnList && btnList.forEach(btn => {
      if ((btn.parentNode.className || '').includes('disabled')) {
        btn.disabled = true
      } else {
        btn.removeAttribute('disabled')
      }
    })
  }

  initInputChange () {
    if (this.hasInput()) {
      this.disableInput()
      return
    }
    this.input = document.createElement('input')
    this.input.type = 'file'
    if (this.multi_selection) {
      this.input.multiple = 'multiple'
    }
    this.input.accept = this.getFileAccept().acceptStr
    this.input.style.display = 'none'
    this.input.className = 'upload_file_input'
    const btn = this.getBrowseBtn()
    this.disableInput()
    // this.removeAllListener()
    if (btn) {
      btn.parentNode.insertBefore(this.input, btn.nextSibling)
      btn.addEventListener('click', this.makeInputChange)
    }
    this.input.addEventListener('change', this.onInputChange)
  }

  async addFile (file,fileName, isresend) {
    // isresend 网络中断后重新发送
    if (isresend) {
      setTimeout(() => this.start())
    } else {
      if (!this.files) {
        this.files = []
      }
      if (this.files.every(file => get(file, 'name') !== file.name)) {
        const { putUrl } = await this.getUploadUrl(file.fileKey)
        file.putUrl = putUrl
        if (!this.multipart_params) {
          this.multipart_params = {}
        }
        this.files.push(file)
        setTimeout(() => this.start())
      }
    }
  }

  async getUploadUrl (key) {
    const req = this.getOption('uptoken_url')
    req.data.key = key
    const res = await send.fetch(req, 'get', true)
    return {
      putUrl: res.putUrl
    }
  }

  hasInput () {
    const btn = this.getBrowseBtn()
    if (!btn) {
      return false
    }
    return btn.parentNode.querySelectorAll('.upload_file_input')[0]
  }

  removeAllListener () {
    const btn = this.getBrowseBtn()
    const input = btn.parentNode.querySelectorAll('.upload_file_input')[0]
    btn.parentNode.removeChild(input)
  }

  makeInputChange = () => {
    this.input.click()
  }

  filesToArr (fileList) {
    const len = fileList.length
    const arr = []
    for (let i = 0; i < len; i++) {
      arr.push(fileList.item(i))
    }
    arr.length = len
    return arr
  }

  onInputChange = (e) => {
    this.fileEvent = e
    if (this.isUploading) {
      const tipMap = {
        'zh_CN': '正在进行文件上传，请稍后再试',
        'zh_TW': '正在進行文件上傳，請稍後再試',
        'en': 'Is being uploaded. Please try again later'
      }
      Message.error(tipMap[window.localStorage.getItem('lang') || 'zh_CN'])
      return
    }
    this.files = Array.from(this.input.files)
    const { acceptStr, suffixStr } = this.getFileAccept()
    const notAcceptFile = this.files.find(i => !(acceptStr || '').includes(this.getSuffix(i.name)) && !(suffixStr || '').includes(this.getSuffix(i.name)))
    if (notAcceptFile && acceptStr) {
      e.target.value = null
      this.trigger('Error', {
        file: notAcceptFile,
        code: -601
      })
      return
    }
    this.files.forEach(i => {
      i.mSize = i.size / (1024 * 1024)
    })
    if (this.max_file_size) {
      const overSizeFile = this.files.find(i => i.mSize > +this.max_file_size.split('mb')[0])
      if (overSizeFile) {
        e.target.value = null
        this.trigger('Error', {
          file: overSizeFile,
          code: -600
        })
        return
      }
      this.files = this.files.filter(i => i.mSize <= +this.max_file_size.split('mb')[0])
    }
    if (this.multi_number) {
      this.files = this.files.filter((i, m) => m < this.multi_number)
    }
    this.onFilter()
    this.trigger('FilesAdded', this.files)
  }

  getFileAccept () {
    if (!Object.keys(this.filters || {}).length) {
      return ''
    }
    const { mime_types: mimeTypes } = this.filters
    const filterMimeArr = mimeTypes.map(i => i.extensions).join().split(',')
    return {
      // acceptStr: filterMimeArr.map(i => CONTENT_TYPE_MAP[i]).join(),
      acceptStr: filterMimeArr.map(i => `.${i}`).join(),
      suffixStr: mimeTypes.map(i => i.extensions).join() || ''
    }
  }

  onFilter () {
    if (!Object.keys(this.filters || {}).length) {
      return
    }
    const { mime_types: mimeTypes, max_file_size: maxFileSize, prevent_duplicates: preventDuplicates } = this.filters
    const filterMimeStr = mimeTypes.map(i => i.extensions).join()
    if (mimeTypes) {
      this.files = this.files.filter(i => filterMimeStr.includes(this.getSuffix(i.name)))
    }
    if (maxFileSize) {
      this.files = this.files.filter(i => i.size < maxFileSize)
    }
    if (preventDuplicates) {
      this.files = this.files.reduce((pre, cur) => {
        if (pre.length === 0) {
          pre.push(cur)
        } else {
          (pre.every(i => i.id !== cur.id)) && (pre.push(cur))
        }
        return pre
      }, [])
    }
  }

  async onUploadFiles () {
    for (let file of this.files) {
      this.trigger('BeforeUpload', file)
      await this.onUpload(file)
    }
  }

  buildUrl (url, items) {
    let res = url
    let query = ''

    Object.entries(items).forEach(([name, value]) => {
      query += (query ? '&' : '') + encodeURIComponent(name) + '=' + encodeURIComponent(value)
    })

    if (query) {
      res += (res.indexOf('?') > 0 ? '&' : '?') + query
    }

    return res
  }

  getSuffix (url) {
    const cmpUrl = url.split('?')[0]
    const list = cmpUrl.split('.')
    return list[list.length - 1].toLowerCase()
  }

  async onUpload (file) {
    return new Promise((resolve, reject) => {
      // eslint-disable-next-line no-undef
      const xhr = new XMLHttpRequest()
      if (file.fileKey) {
        this.multipart_params.key = file.fileKey
      }
      xhr.open(this.http_method, this.buildUrl(file.putUrl, this.multipart_params || {}), true)
      const suffix = this.getSuffix(file.name)
      const contentType = CONTENT_TYPE_MAP[suffix] || 'application/octet-stream'
      this.headers && Object.entries(this.headers).forEach(([key, val]) => {
        xhr.setRequestHeader(key, val)
      })
      xhr.setRequestHeader('content-Type', `${contentType};charset=UTF-8`)
      this.isUploading = true
      xhr.upload.onprogress = (e) => {
        if (e.lengthComputable) {
          const percent = Math.ceil((e.loaded / e.total) * 100)
          file.percent = percent
          this.trigger('UploadProgress', file)
        }
      }
      xhr.send(file)
      let hasComplete = false
      xhr.upload.onload = () => {
        this.trigger('UploadComplete', file, xhr.responseText)
        hasComplete = true
      }
      xhr.upload.onerror = () => {
        this.trigger('Error', file)
        Message.error(m7Language('webchat.uploadError'))
      }
      xhr.onreadystatechange = () => {
        if (xhr.readyState === 4 && xhr.status === 200) {
          if (xhr.status === 200) { // 上传文件成功
            this.trigger('FileUploaded', file, xhr.responseText)
            !hasComplete && this.trigger('UploadComplete', file, xhr.responseText)
            resolve()
          } else { // 上传文件失败
            this.trigger('Error', {
              file,
              code: 4
            })
            this.trigger('UploadComplete', file, xhr.responseText)
            reject()
          }
          this.fileEvent && this.fileEvent.target && (this.fileEvent.target.value = '')
          this.files = []
          // this.removeAllListener()
          this.isUploading = false
        }
      }
    })
  }

  trigger (handleName, file, ...arg) {
    this.bindMap[handleName] && this.bindMap[handleName](this, file, ...arg)
    this.initMap[handleName] && this.initMap[handleName](this, file, ...arg)
  }

  removeFile (file) {
    this.files = this.files.filter(i => i.id !== file.id)
  }

  async start () {
    await this.onUploadFiles()
  }
  bind (handleName, cb) {
    if (!this.bindMap) {
      this.bindMap = {}
    }
    this.bindMap[handleName] = cb
  }
  getOption (name) {
    return this[name]
  }
  setOption (data, value) {
    if (typeof data === 'string') {
      this[data] = value
    } else {
      Object.entries(data).forEach(([key, val]) => {
        this[key] = val
      })
    }
  }
  destroy () {

  }
  refresh () {

  }
}
